/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of LINAL (LINear ALgebra)
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/
#include "linal/common.hxx"
#include "linal/util.hxx"

#include "linal/format.hxx"

#include "linal/device/host/operation.hxx"


namespace linal      {
  namespace host   {
    namespace internal {
      // BLAS 1 and Utils

      /*! Extract diagonal matrix A and store it as column vector to d
      */
      int get_diag(FLA_Obj &A, FLA_Obj &d) {
        LINAL_ERROR_DETAIL( A.m == d.m,
                            "%s\n %s %d\n %s %d\n",
                            LINAL_ERROR_MATRIX_DIMENSION_MISMATCH,
                            "A length = ", A.m,
                            "d length = ", d.m );

        Flat_ AA(A), dd(d), alpha, delta;

#pragma unroll
        for (int i=0;i<AA.get_m();++i) {
          AA.extract( alpha, 1, 1, i, i );
          dd.extract( delta, 1, 1, i, 0 );
          FLA_Set( ~alpha, ~delta );
        }

        return true;
      }

      /*! Set a vector to diagonal entrees of the matrix.
      */
      int set_diag(FLA_Obj &d, FLA_Obj &A) {
        LINAL_ERROR_DETAIL( A.m == d.m,
                            "%s\n %s %d\n %s %d\n",
                            LINAL_ERROR_MATRIX_DIMENSION_MISMATCH,
                            "A length = ", A.m,
                            "d length = ", d.m );

        Flat_ AA(A), dd(d), alpha, delta;

#pragma unroll
        for (int i=0;i<AA.get_m();++i) {
          AA.extract( alpha, 1, 1, i, i );
          dd.extract( delta, 1, 1, i, 0 );
          FLA_Set( ~delta, ~alpha );
        }

        return true;
      }

      /*
      int invert(int conj, FLA_Obj &x) {
        FLA_Invert( conj, x );
        return true;
      }
      int conjugate(FLA_Obj &A) {
        FLA_Conjugate(A);
        return true;
      }

      int hermitianize(int uplo, FLA_Obj &A) {
        FLA_Hermitianize( uplo, A );
        return true;
      }
      int symmetrize(int uplo, FLA_Obj &A) {
        FLA_Symmetrize( uplo, A );
        return true;
      }
      */

      // BLAS 3
      int gemm( int transa, int transb,
                FLA_Obj &alpha,  FLA_Obj &A, FLA_Obj &B,
                FLA_Obj &beta,   FLA_Obj &C ) {
        FLA_Gemm( transa, transb, alpha, A, B, beta,  C );
        return true;
      }
      int herk( int uplo,  int trans,
                FLA_Obj &alpha,  FLA_Obj &A,
                FLA_Obj &beta,   FLA_Obj &C ) {
        FLA_Herk( uplo, trans, alpha, A, beta, C );
        return true;
      }
      int trmm( int side,  int uplo,  int trans,  int diag,
                FLA_Obj &alpha,  FLA_Obj &A,  FLA_Obj &B ) {
        FLA_Trmm( side, uplo, trans, diag, alpha, A, B );
        return true;
      }
      int trsm( int side,  int uplo,  int trans,  int diag,
                FLA_Obj &alpha,  FLA_Obj &A,  FLA_Obj &B ) {
        FLA_Trsm( side, uplo, trans, diag, alpha, A, B ); 
        return true;
      }
      int syrk( int uplo,  int trans,
                FLA_Obj &alpha,  FLA_Obj &A,
                FLA_Obj &beta,   FLA_Obj &C ) {
        FLA_Syrk( uplo, trans, alpha, A, beta, C );
        return true;
      }

    }
  }
}
